#ifndef XG_STD_CPP
#define XG_STD_CPP
///////////////////////////////////////////////////////////////////
#include <thread>
#include <typeinfo>

#include "../cmd.h"
#include "../Reflect.h"
#include "../Sharemem.h"

#ifdef XG_LINUX

#include <dirent.h>
#include <sys/stat.h>
#include <sys/wait.h>

int GetLastError()
{
	return errno;
}
string GetErrorString(int errcode)
{
	return strerror(errcode);
}

#else

string GetErrorString(int errcode)
{
	string msg;
	HLOCAL LocalAddress = NULL;

	FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, errcode, 0, (LPSTR)(&LocalAddress), 0, NULL);
	if (LocalAddress == NULL) return msg;
	msg = (LPSTR)(LocalAddress);
	LocalFree(LocalAddress);

	return stdx::trim(stdx::utfcode(msg));
}

#endif

class Runnable : public WorkItem
{
protected:
	long long etime;
	function<void()> func;

public:
	void run()
	{
		func();
	}
	bool runnable()
	{
		return etime <= 0 || GetTime() > etime;
	}
	void setDelay(int delay)
	{
		etime = delay > 0 ? GetTime() + delay * 1000 : 0;
	}
	Runnable(function<void()> run)
	{
		func = run;
		etime = 0;
	}
	Runnable(function<void()> run, int delay)
	{
		func = run;
		setDelay(delay);
	}
};

string GetErrorString()
{
	return GetErrorString(GetLastError());
}

Object::~Object()
{
}
string Object::toString() const
{
	char buffer[16];

	sprintf(buffer, "%p", this);

	return buffer;
}
const char* Object::getClassName() const
{
#ifdef _MSC_VER
	return typeid(*this).name() + 6;
#else
	return SkipStartString(typeid(*this).name(), "0123456789");
#endif
}
string Object::attrs(const string& exclude) const
{
	StringCreator out;
	vector<ReflectItem> vec = ReflectHelper::GetAttrList(this);

	if (exclude.empty())
	{
		for (ReflectItem& item : vec)
		{
			out << "," << item.getName();
		}
	}
	else
	{
		char tag[1024];
		string exlist = "," + exclude + ",";

		for (ReflectItem& item : vec)
		{
			sprintf(tag, ",%s,", item.getName());

			if (exlist.find(tag) == string::npos)
			{
				out << "," << item.getName();
			}
		}
	}

	const string& res = out.getContent();

	return res.empty() ? res : res.substr(1);
}

string Exception::toString() const
{
	return stdx::format("%s[%d][%s]", getClassName(), errcode, errmsg.c_str());
}
const char* Exception::what() const noexcept
{
	return errmsg.c_str();
}

int stdx::atoi(const char* str)
{
	int n;
	int sign;
	const char* p = str;

	if (str == NULL || *str == 0) return 0;

	while (isspace(*p)) p++;

	sign = ('-' == *p) ? -1 : 1;

	if ('+' == *p || '-' == *p) p++;

	for (n = 0; isdigit(*p); p++) n = 10 * n + (*p - '0');

	return sign * n;
}
double stdx::atof(const char* str)
{
	return str && *str ? ::atof(str) : 0.0;
}
long long stdx::atol(const char* str)
{
	long long n;
	long long sign;
	const char* p = str;

	if (str == NULL || *str == 0) return 0;

	while (isspace(*p)) p++;

	sign = ('-' == *p) ? -1 : 1;

	if ('+' == *p || '-' == *p) p++;

	for (n = 0; isdigit(*p); p++) n = 10 * n + (*p - '0');

	return sign * n;
}
string stdx::format(const char* fmt, ...)
{
	int len;
	string str;
	va_list args;

	va_start(args, fmt);
	len = vformat(str, fmt, args);
	va_end(args);

	return std::move(str);
}
int stdx::format(string& str, const char* fmt, ...)
{
	int len;
	va_list args;

	str.clear();

	va_start(args, fmt);
	len = vformat(str, fmt, args);
	va_end(args);

	return len;
}
int stdx::append(string& str, const string& msg)
{
	return (str += msg).length();
}
int stdx::append(string& str, const char* fmt, ...)
{
	int len;
	va_list args;

	va_start(args, fmt);
	len = vformat(str, fmt, args);
	va_end(args);

	return len;
}
int stdx::vformat(string& str, const char* fmt, va_list args)
{
	int len;
	va_list dest;
	char buffer[64 * 1024];

	va_copy(dest, args);
	len = vsnprintf(buffer, sizeof(buffer), fmt, args);

	if (len > 0 && len < sizeof(buffer))
	{
		str += buffer;

		return len;
	}

	SmartBuffer tmp(len + 1);

	len = vsnprintf(tmp.str(), tmp.size(), fmt, dest);

	if (len > 0 && len < tmp.size()) str += tmp.str();

	return len;
}

const char* stdx::GetProcessExePath()
{
	DWORD dw = MAX_PATH;
	static char buffer[4 * MAX_PATH];

	if (isalpha(*buffer)) return buffer;

#ifdef XG_LINUX
	sprintf(buffer, "/proc/%d/exe", getpid());

	if (readlink(buffer, buffer, sizeof(buffer)) < 0) return NULL;
#else
	char* ptr = buffer;

	if (QueryFullProcessPath)
	{
		dw = QueryFullProcessPath(GetCurrentProcess(), 0, buffer, &dw);
	}
	else
	{
		dw = GetModuleFileNameExA(GetCurrentProcess(), NULL, buffer, dw);
	}

	if (dw <= 0) return NULL;

	while (*ptr)
	{
		if (*ptr == '\\') *ptr = '/';

		++ptr;
	}

	strncpy(buffer, stdx::utfcode(buffer).c_str(), sizeof(buffer) - 1);
#endif

	return buffer;
}
const string& stdx::EmptyString()
{
	static string str;

	return str;
}
string stdx::DecodeURL(const string& msg)
{
	size_t len = msg.length();

	if (len == 0) return msg;

	if (len <= XG_CHARVALUE_BUFLEN)
	{
		char buffer[XG_CHARVALUE_BUFLEN];

		return URLDecode(msg.c_str(), len, buffer);
	}

	SmartBuffer buffer(len);

	return URLDecode(msg.c_str(), len, buffer.str());
}
string stdx::EncodeURL(const string& msg)
{
	size_t len = msg.length();

	if (len == 0) return msg;
	
	if (len <= XG_CHARVALUE_BUFLEN)
	{
		char buffer[XG_CHARVALUE_BUFLEN * 4 + sizeof(char)];

		return URLEncode(msg.c_str(), len, buffer);
	}

	SmartBuffer buffer(len * 4 + sizeof(char));

	return URLEncode(msg.c_str(), len, buffer.str());
}
SmartBuffer stdx::DecodeHex(const char* src, int len)
{
	if ((len /= 2) <= 0) return SmartBuffer();

	SmartBuffer dest(len);

	for (int i = 0, j = 0; i < len; i++, j++)
	{
		if (src[j] <= '9')
		{
			dest[i] = (src[j] - '0') * 16;
		}
		else if (src[j] >= 'a')
		{
			dest[i] = (src[j] - 'a' + 10) * 16;
		}
		else
		{
			dest[i] = (src[j] - 'A' + 10) * 16;
		}

		++j;

		if (src[j] <= '9')
		{
			dest[i] += src[j] - '0';
		}
		else if (src[j] >= 'a')
		{
			dest[i] += src[j] - 'a' + 10;
		}
		else
		{
			dest[i] += src[j] - 'A' + 10;
		}
	}

	return dest;
}
SmartBuffer stdx::EncodeHex(const void* src, int len, bool upper)
{
	SmartBuffer dest(len + len);
	u_char* data = (u_char*)(src);

	if (upper)
	{
		for (int i = 0; i < len; i++) sprintf(dest.str() + (i * 2), "%02X", data[i]);
	}
	else
	{
		for (int i = 0; i < len; i++) sprintf(dest.str() + (i * 2), "%02x", data[i]);
	}

	return dest;
}
int stdx::GetFileContent(string& content, const string& path)
{
	int sz;
	SmartBuffer buffer;

	if ((sz = GetFileContent(buffer, path)) > 0) content = string(buffer.str(), sz);

	return sz;
}
int stdx::GetFileContent(SmartBuffer& content, const string& path)
{
	XFile file;
	
	if (!file.open(path, eREAD)) return XG_SYSERR;

	int sz = file.size();

	if (sz <= 0) return sz;

	sz = min(256 * 1024 * 1024, sz);

	if ((sz = file.read(content.malloc(sz), sz)) < 0) return sz;

	if (sz < content.size()) content.truncate(sz);

	return sz;
}

bool stdx::async(sp<WorkItem> item)
{
	static TaskQueue* queue = TaskQueue::Instance();

	return queue->push(item);
}
bool stdx::async(function<void()> func)
{
	sp<WorkItem> item = newsp<Runnable>(func);

	return async(item);
}
bool stdx::timer(int delay, sp<WorkItem> item)
{
	return stdx::delay(delay, item, 0);
}
bool stdx::timer(int delay, function<void()> func)
{
	return stdx::delay(delay, func, 0);
}
bool stdx::delay(int delay, sp<WorkItem> item, int maxtimes)
{
	static TimerTaskQueue* queue = TimerTaskQueue::Instance();

	return queue->push(item, delay > 0 ? delay : 1, maxtimes);
}
bool stdx::delay(int delay, function<void()> func, int maxtimes)
{
	sp<WorkItem> item = newsp<Runnable>(func);

	return stdx::delay(delay, item, maxtimes);
}

#ifdef XG_LINUX

#include <iconv.h>

static int IconvTranslate(const char* fromcode, const char* tocode, const char* src, int srclen, char* dest, int destlen)
{
	if (srclen <= 0) srclen = strlen(src);

	if (fromcode == NULL) fromcode = GetLocaleCharset();
	if (tocode == NULL) tocode = GetLocaleCharset();

	iconv_t handle = iconv_open(tocode, fromcode);
	
	if (handle == (iconv_t)(-1)) return XG_ERROR;

	char* str = dest;
	size_t insz = (size_t)(srclen);
	size_t outsz = (size_t)(destlen);
	size_t val = iconv(handle, (char**)(&src), &insz, &str, &outsz);
	
	if (val == (size_t)(-1))
	{
		iconv_close(handle);
	
		return XG_ERROR;
	}

	iconv_close(handle);

	int len = str - dest;
	
	if (len < destlen) *str = 0;

	return len;
}

string stdx::syscode(const string& str)
{
	return str;
}
string stdx::gbkcode(const string& str)
{
	int len = str.length();
	SmartBuffer res(len + sizeof(char));

	return IconvTranslate("UTF-8", "GBK", str.c_str(), len, res.str(), res.size()) < 0 ? str : res.str();
}
string stdx::utfcode(const string& str)
{
	int len = str.length();
	SmartBuffer res(6 * len + sizeof(char));

	return IconvTranslate("GBK", "UTF-8", str.c_str(), len, res.str(), res.size()) < 0 ? str : res.str();
}

#else

string stdx::syscode(const string& str)
{
	return stdx::gbkcode(str);
}
string stdx::gbkcode(const string& str)
{
	int len;
	string res;

	len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);

	if (len <= 0) return str;

	wchar_t* wstr = new wchar_t[len + 1];
	MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, wstr, len);
	len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);

	if (len <= 0)
	{
		delete[] wstr;

		return str;
	}

	char* dest = new char[len + 1];
	WideCharToMultiByte(CP_ACP, 0, wstr, -1, dest, len, NULL, NULL);
	dest[len] = 0;
	res = dest;

	delete[] dest;
	delete[] wstr;

	return std::move(res);
}
string stdx::utfcode(const string& str)
{
	int len;
	string res;

	len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);

	wchar_t* wstr = new wchar_t[len + 1];
	MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, wstr, len);
	len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);

	char* dest = new char[len + 1];
	WideCharToMultiByte(CP_UTF8, 0, wstr, -1, dest, len, NULL, NULL);
	dest[len] = 0;
	res = dest;

	delete[] wstr;
	delete[] dest;

	return std::move(res);
}

#endif

string stdx::translate(const string& str)
{
	if (str.empty()) return str;

	int res = 0;
	string val = str;
	char buffer[64 * 1024];

#ifdef XG_LINUX
	if (val.find('$') == string::npos && val.find('`') == string::npos) return val;
#else
	if (val.find('$') == string::npos)
	{
		if (val.find('%') == string::npos) return val;
	}
	else
	{
		string tmp;
		vector<string> vec;

		stdx::split(vec, val, "/");
		
		for (string& item : vec)
		{
			tmp += "/";

			size_t pos = item.find('$');
			
			if (pos == string::npos)
			{
				tmp += item;
			}
			else
			{
				if (pos > 0) tmp += item.substr(0, pos);

				tmp += "%" + trim(item.substr(pos + 1), "{()}") + "%";
			}
		}

		val = tmp.substr(1);
	}
#endif
	if ((res = cmdx::RunCommand("echo " + val, buffer, sizeof(buffer))) > 0)
	{
		if (buffer[res - 1] == '\n')
		{
			if (res > 1 && buffer[res - 2] == '\r')
			{
				buffer[res -= 2] = 0;
			}
			else
			{
				buffer[res -= 1] = 0;
			}
		}

		val = buffer;
	}

	val = stdx::replace(val, "\\r", "\r");
	val = stdx::replace(val, "\\n", "\n");
	val = stdx::replace(val, "\\t", "\t");

	return std::move(val);
}
string stdx::replace(const string& str, const string& src, const string& dest)
{
	if (str.empty()) return str;

	string res;
	const char* q = NULL;
	const char* p = str.c_str();

	while (*p)
	{
		if ((q = strstr(p, src.c_str())) == NULL)
		{
			res.append(p);

			break;
		}

		res.append(p, q);
		res.append(dest);
		p = q + src.length();
	}

	return std::move(res);
}
string stdx::GetParentPath(const string& path)
{
	if (path.empty()) return path;

	string res;
	size_t len = path.length();

	if (path.back() == '/' || path.back() == '\\')
	{
		res = path.substr(0, len - 1);
	}
	else
	{
		res = path;
	}

	if ((len = res.rfind('/')) == string::npos && (len = res.rfind('\\')) == string::npos) return stdx::EmptyString();

	return res.substr(0, len);
}
string stdx::GetExtNameFromPath(const string& path)
{
	if (path.empty()) return path;

	const char* end = NULL;
	const char* str = strrchr(path.c_str(), '.');
	
	if (str == NULL) return stdx::EmptyString();
	
	for (end = ++str; *end && isalnum(*end); end++){}
	
	return *end ? stdx::EmptyString() : str;
}
string stdx::GetFileNameFromPath(const string& path)
{
	if (path.empty()) return path;

	const char* name = strrchr(path.c_str(), '/');

	if (name == NULL && (name = strrchr(path.c_str(), '\\')) == NULL) return path;

	return name + 1;
}
string stdx::trim(const string& str, const string& space)
{
	if (str.empty()) return str;

	int len = str.length();

	const char* head = str.c_str();
	const char* tail = str.c_str() + len - 1;

	while (head <= tail && strchr(space.c_str(), *head)) ++head;

	if (*head == 0) return stdx::EmptyString();

	while (tail >= head && strchr(space.c_str(), *tail)) --tail;

	return string(head, tail + 1);
}
string stdx::fill(const string& str, int len, bool left, char ch)
{
	string dest;

	int cnt = len - str.length();

	if (cnt == 0) return str;

	if (cnt < 0) return left ? str.substr(-cnt) : str.substr(0, len);

	if (left)
	{
		for (int i = 0; i < cnt; i++) dest.push_back(ch);

		dest += str;
	}
	else
	{
		dest = str;

		for (int i = 0; i < cnt; i++) dest.push_back(ch);
	}

	return std::move(dest);
}
vector<string> stdx::split(const string& str, const string& space)
{
	vector<string> vec;

	stdx::split(vec, str, space);

	return std::move(vec);
}
int stdx::split(vector<string>& vec, const string& str, const string& space)
{
	vec.clear();

	if (str.length() > 0)
	{
		const char* end = NULL;
		const char* start = str.c_str();

		while (true)
		{
			if ((end = strstr(start, space.c_str())) == NULL)
			{
				vec.push_back(start);

				break;
			}

			vec.push_back(string(start, end));
			start = end + space.length();
		}
	}

	return vec.size();
}

int stdx::FindFile(vector<string>& vec, const string& path, const string& filter)
{
	string cmd;
	vector<string> tmp;
	SmartBuffer buffer(4 * 1024 * 1024);

	vec.clear();

#ifdef XG_LINUX
	stdx::format(cmd, "find %s -name %s", stdx::quote(path).c_str(), stdx::quote(filter).c_str());
#else
	stdx::format(cmd, "for /r %s %%i in (%s) do @echo %%i", stdx::quote(path).c_str(), filter.c_str());
#endif
	if (cmdx::RunCommand(cmd, buffer.str(), buffer.size()) < 0) return XG_SYSERR;

	stdx::split(tmp, buffer.str(), "\n");

	for (auto& str : tmp)
	{
		str = stdx::trim(str);

		if (str.length() > 0) vec.push_back(stdx::replace(str, "\\", "/"));
	}

	return vec.size();
}
int stdx::GetFolderContent(vector<string>& vec, const string& path, int flag, bool containdots)
{
	vec.clear();

	return GetFolderContent([&](string path, int type, int size, time_t mtime){
		vec.push_back(path);
	}, path, flag, containdots);
}

#ifdef XG_LINUX

int stdx::GetFolderContent(function<void(string, int, long, time_t)> func, const string& path, int flag, bool containdots)
{
	string str;
	int res = 0;
	DIR* dir = NULL;
	char buf[MAX_PATH];
	int len = path.length();

	if ((dir = opendir(path.c_str())) == NULL) return XG_ERROR;

	strcpy(buf, path.c_str());

	if (buf[len - 1] != '/' && buf[len - 1] != '\\')
	{
		buf[len++] = '/';
		buf[len] = 0;
	}

	for (struct dirent* ent = readdir(dir); ent; ent = readdir(dir))
	{
		struct stat statbuf;
		const char* name = ent->d_name;

		if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue;
		
		str = buf;
		str += name;

		if (lstat(str.c_str(), &statbuf) < 0)
		{
			closedir(dir);

			return XG_ERROR;
		}

		if (flag == eNONE)
		{
			if (containdots || *name != '.')
			{
				func(name, S_ISDIR(statbuf.st_mode) ? ePATH : eFILE, statbuf.st_size, statbuf.st_mtime);

				res++;
			}
		}
		else if (flag == ePATH)
		{
			if (S_ISDIR(statbuf.st_mode))
			{
				if (containdots || *name != '.')
				{
					func(name, ePATH, statbuf.st_size, statbuf.st_mtime);

					res++;
				}
			}
		}
		else
		{
			if (S_ISDIR(statbuf.st_mode)) continue;

			if (containdots || *name != '.')
			{
				func(name, eFILE, statbuf.st_size, statbuf.st_mtime);

				res++;
			}
		}
	}

	closedir(dir);

	return res;
}

#else

int stdx::GetFolderContent(function<void(string, int, long, time_t)> func, const string& path, int flag, bool containdots)
{
	int res = 0;
	HANDLE handle;
	string str = path;
	char buf[MAX_PATH];
	int len = str.length();
	WIN32_FIND_DATAA findData;

	auto mtime = [&](){
		struct tm t;
		FILETIME ft;
		SYSTEMTIME st;
		SYSTEMTIME utc;

		if (FileTimeToSystemTime(&findData.ftLastWriteTime, &utc))
		{
			SystemTimeToTzSpecificLocalTime(NULL, &utc, &st);

			t.tm_year = st.wYear - 1900;
			t.tm_mon = st.wMonth - 1;
			t.tm_mday = st.wDay;

			t.tm_sec = st.wSecond;
			t.tm_min = st.wMinute;
			t.tm_hour = st.wHour;

			return mktime(&t);
		}

		return (time_t)(0);
	};

	strcpy(buf, path.c_str());

	if (buf[len - 1] == '/' || buf[len - 1] == '\\')
	{
		str += "*";
	}
	else
	{
		str += "/*";
		strcat(buf, "/");
	}

	handle = FindFirstFileA(stdx::syscode(str).c_str(), &findData);

	if (INVALID_HANDLE_VALUE == handle) return XG_ERROR;

	while (FindNextFileA(handle, &findData))
	{
		const char* name = findData.cFileName;

		if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue;

		if (flag == eNONE)
		{
			if (containdots || *name != '.')
			{
				if (FILE_ATTRIBUTE_DIRECTORY & findData.dwFileAttributes)
				{
					func(stdx::utfcode(name), ePATH, findData.nFileSizeLow, mtime());
				}
				else
				{
					func(stdx::utfcode(name), eFILE, findData.nFileSizeLow, mtime());
				}

				res++;
			}
		}
		else if (flag == ePATH)
		{
			if (FILE_ATTRIBUTE_DIRECTORY & findData.dwFileAttributes)
			{
				if (containdots || *name != '.')
				{
					func(stdx::utfcode(name), ePATH, findData.nFileSizeLow, mtime());

					res++;
				}
			}
		}
		else
		{
			if ((FILE_ATTRIBUTE_DIRECTORY & findData.dwFileAttributes) == 0)
			{
				if (containdots || *name != '.')
				{
					func(stdx::utfcode(name), eFILE, findData.nFileSizeLow, mtime());

					res++;
				}
			}
		}
	}

	FindClose(handle);

	return res;
}

#endif

void Buffer::free()
{
	if (buf)
	{
		delete[] buf;
		buf = NULL;
		maxsz = 0;
		sz = 0;
	}
}
u_char* Buffer::malloc(int sz)
{
	this->free();

	buf = new u_char[sz + 1];
	this->maxsz = sz;
	this->sz = sz;
	buf[sz] = 0;

	return buf;
}
u_char* Buffer::truncate(int sz)
{
	if (this->maxsz >= sz)
	{
		this->sz = sz;
		buf[sz] = 0;

		return buf;
	}

	u_char* tmp = new u_char[sz + 1];

	if (buf)
	{
		memcpy(tmp, buf, this->sz);
		delete[] buf;
	}

	tmp[this->sz] = 0;
	this->maxsz = sz;
	this->sz = sz;
	tmp[sz] = 0;
	buf = tmp;

	return buf;
}

SmartBuffer SmartBuffer::clone()
{
	int sz = size();
	SmartBuffer data;

	if (sz > 0) memcpy(data.malloc(sz), ptr(), sz);

	return data;
}
u_char* SmartBuffer::malloc(int sz)
{
	buffer = newsp<Buffer>(sz);

	return buffer->ptr();
}
SmartBuffer& SmartBuffer::append(const string& str)
{
	int sz = size();
	int len = str.length();

	if (len > 0 && truncate(sz + len)) memcpy(ptr() + sz, str.c_str(), len);

	return *this;
}
SmartBuffer& SmartBuffer::append(const SmartBuffer& obj)
{
	int sz = size();

	if (buffer == obj.buffer)
	{
		if (sz > 0 && truncate(sz << 1)) memcpy(ptr() + sz, ptr(), sz);
	}
	else
	{
		if (truncate(sz + obj.size())) memcpy(ptr() + sz, obj.str(), obj.size());
	}

	return *this;
}
SmartBuffer& SmartBuffer::operator = (const string& val)
{
	int sz = val.length();

	if (sz <= 0)
	{
		free();

		return *this;
	}

	if (sz > size()) malloc(sz);

	memcpy(str(), val.c_str(), sz);
	truncate(sz);

	return *this;
}

bool Thread::start()
{
	CATCH_EXCEPTION({
		std::thread(std::bind(&Thread::run, this)).detach();

		return true;
	});

	return false;
}

void TaskQueue::run()
{
	++threadcount;

	while (threads > 0)
	{
		sp<WorkItem> item;
		size_t len = pop(item);

		if (len > 0)
		{
			CATCH_EXCEPTION({
				if (item->runnable())
				{
					item->run();
				}
				else
				{
					if (len < 100) Sleep(1);

					mtx.lock();
					queue.push(item);
					mtx.unlock();
				}
			});

			--curdepth;
		}
		else
		{
			if (threadcount > threads)
			{
				SpinLocker lk(mtx);

				if (threadcount > threads)
				{
					removeDestroyFunc();

					--threadcount;

					return;
				}
			}

			Sleep(1);
		}
	}

	--threadcount;
}
bool TaskQueue::push(sp<WorkItem> item)
{
	CHECK_FALSE_RETURN(item);

	{
		SpinLocker lk(mtx);
		size_t len = queue.push(item);

		if (len > 0)
		{
			size_t num = threads + threads;

			if (len > num && threadcount < num)
			{
				CATCH_EXCEPTION({
					std::thread(std::bind(&TaskQueue::run, this)).detach();
				});
			}

			++curdepth;

			return true;
		}
	}

	CHECK_FALSE_RETURN(threads == 0 && start());

	SpinLocker lk(mtx);

	CHECK_FALSE_RETURN(queue.push(item));

	++curdepth;

	return true;
}
void TaskQueue::cancelDestroyFunc()
{
	long key = GetCurrentThreadId();
	SpinLocker lk(desmtx);
	destroymap.erase(key);
}
bool TaskQueue::removeDestroyFunc()
{
	function<void()> func;
	long key = GetCurrentThreadId();

	{
		SpinLocker lk(desmtx);
		auto it = destroymap.find(key);

		if (it == destroymap.end()) return false;

		func = it->second;
		destroymap.erase(it);
	}

	func();

	return true;
}
bool TaskQueue::trySetDestroyFunc(function<void()> func)
{
	long key = GetCurrentThreadId();
	SpinLocker lk(desmtx);
	auto it = destroymap.find(key);

	CHECK_FALSE_RETURN(it == destroymap.end());

	destroymap[key] = func;

	return true;
}
bool TaskQueue::start(size_t threads, size_t maxsz)
{
	SpinLocker lk(mtx);

	if (queue.capacity() == 0) queue.init(maxsz);

	this->threads = threads;

	CATCH_EXCEPTION({
		while (threads-- > 0) std::thread(std::bind(&TaskQueue::run, this)).detach();

		return true;
	});

	return false;
}
TaskQueue* TaskQueue::Instance()
{
	XG_DEFINE_GLOBAL_VARIABLE(TaskQueue)
}

bool TimerTaskQueue::Task::update(long long now)
{
	if (++times >= maxtimes && maxtimes > 0) return false;

	if (delay > 0)
	{
		rtime = now + delay;
	}
	else
	{
		rtime = now - delay - now % (24 * 3600 * 1000);

		if (rtime <= now) rtime += 24 * 3600 * 1000;
	}

	return true;
}
bool TimerTaskQueue::get(Task& task)
{
	long long now = GetTime() / 1000;
	
	if (now <= 0) return false;

	SpinLocker lk(mtx);

	if (queue.empty()) return false;

	task = queue.top();

	if (now < task.rtime) return false;

	queue.pop();

	if (task.update(now)) queue.push(task);

	return true;
}

void TimerTaskQueue::run()
{
	Task item;
	TaskQueue* taskqueue = TaskQueue::Instance();

	while (true)
	{
		for (int i = 0; i < 10 && get(item); i++)
		{
			while (!taskqueue->push(item.task)) Sleep(1);
		}

		Sleep(1);
	}
}
void TimerTaskQueue::check(function<bool(sp<WorkItem>)> filter)
{
	SpinLocker lk(mtx);
	priority_queue<Task> tmp;

	while (!queue.empty())
	{
		if (filter(queue.top().task)) tmp.push(queue.top());

		queue.pop();
	}

	std::swap(tmp, queue);
}
bool TimerTaskQueue::push(sp<WorkItem> task, int delay, int maxtimes)
{
	Task item;

	CHECK_FALSE_RETURN(item.init(delay, maxtimes, task));

	mtx.lock();

	if (started == 0)
	{
		std::thread(std::bind(&TimerTaskQueue::run, this)).detach();

		started = XG_OK;
	}

	queue.push(item);

	mtx.unlock();

	return true;
}
bool TimerTaskQueue::daily(sp<WorkItem> task, const string& time, int maxtimes)
{
	DateTime dt = DateTime::FromString(DateTime::ToString().substr(0, 11) + time);

	CHECK_FALSE_RETURN(dt.canUse());

	int delay = (dt.getTime() * 1000) % (24 * 3600 * 1000);

	return push(task, -delay, maxtimes);
}
bool TimerTaskQueue::update(int delay, int maxtimes, function<bool(sp<WorkItem>)> filter)
{
	int res = 0;
	SpinLocker lk(mtx);
	priority_queue<Task> tmp;

	while (!queue.empty())
	{
		Task item = queue.top();

		if (filter(item.task))
		{
			item.init(delay, maxtimes, item.task);
			res++;
		}

		tmp.push(item);
		queue.pop();
	}

	std::swap(tmp, queue);

	return res > 0;
}

TimerTaskQueue* TimerTaskQueue::Instance()
{
	XG_DEFINE_GLOBAL_VARIABLE(TimerTaskQueue)
}
#ifndef XG_LINUX

QueryFullProcessPathFunc QueryFullProcessPath = NULL;

#endif

Process::Process()
{
	srand(time(&startime) + clock());
	argv = NULL;
}
int Process::init(int argc, char** argv)
{
	this->vec.clear();
	this->argv = argv;

	const char* p = strrchr(argv[0], '/');

	if (p == NULL) p = strrchr(argv[0], '\\');

	appname = p ? p + 1 : argv[0];

	size_t pos = appname.rfind('.');

	if (pos != string::npos && pos > 0) appname = appname.substr(0, pos);

	for (int i = 0; i < argc; i++)
	{
		const char* val = argv[i][0] == '\\' ? argv[i] + 1 : argv[i];
#ifdef XG_LINUX
		vec.push_back(val);
#else
		vec.push_back(stdx::utfcode(val));
#endif
	}

	return vec.size();
}
const char* Process::getAppname() const
{
	return appname.c_str();
}
int Process::getCmdParamCount() const
{
	return vec.size();
}
const char* Process::getCmdParam(int index) const
{
	return index < vec.size() ? vec[index].c_str() : NULL;
}
const char* Process::getCmdParam(const string& key) const
{
	if (vec.empty()) return NULL;

	for (size_t i = 0; i < vec.size(); i++)
	{
		if (key == vec[i])
		{
			if (i + 1 >= vec.size()) return "";
			if (vec[i + 1].empty()) return "";

			return vec[i + 1].c_str();
		}
	}

	return NULL;
}
Mutex& Process::getMutex()
{
	return mutex;
}
char** Process::getCmdParam() const
{
	return argv;
}
int Process::getParamCount() const
{
	SpinLocker lk(mtx);
	return paramap.size();
}
void Process::setParam(const string& key, const string& val)
{
	SpinLocker lk(mtx);
	paramap[key] = val;
}
string Process::getParam(const string& key) const
{
	string val;
	SpinLocker lk(mtx);
	auto it = paramap.find(key);

	if (it == paramap.end()) return stdx::EmptyString();

	val = it->second;

	return std::move(val);
}
int Process::getObjectCount() const
{
	SpinLocker lk(mtx);
	return objmap.size();
}
void Process::setObject(const string& key, const void* obj)
{
	SpinLocker lk(mtx);
	objmap[key] = obj;
}
const void* Process::getObject(const string& key) const
{
	SpinLocker lk(mtx);
	auto it = objmap.find(key);

	if (it == objmap.end()) return NULL;

	return it->second;
}
Process* Process::Instance()
{
	static Process* ptr = NULL;

	if (ptr) return ptr;

	Process::GetGlobalVariable("PROCESS_INSTANCE", ptr, true);

	if (ptr == NULL) ErrorExit(XG_SYSERR);

	if (CheckSystemSizeof())
	{
#ifndef XG_LINUX
		HMODULE handle;

		if (handle = LoadLibraryA("KERNEL32.dll"))
		{
			if ((QueryFullProcessPath = (QueryFullProcessPathFunc)GetProcAddress(handle, "QueryFullProcessImageNameA")) == NULL)
			{
				FreeLibrary(handle);
			}
		}
#endif
	}
	else
	{
		ErrorExit(XG_SYSERR);
	}

	return ptr;
}
Process* Process::Instance(int argc, char** argv)
{
	GetLocaleCharset();

	Instance()->init(argc, argv);

	if (stdx::GetProcessExePath() == NULL) ErrorExit(XG_SYSERR);

	return Instance();
}
Application* Process::GetApplication(Application* obj)
{
	static Application* app = (Application*)GetObject("{APPLICATION}");

	if (app == NULL) SetObject("{APPLICATION}", app = obj);

	return app;
}

void Process::CheckSingle()
{
	string exepath;
	vector<ProcessData> vec;

	if (Process::GetProcessExePath(exepath) && Process::GetSystemProcessList(vec) > 0)
	{
		PROCESS_T pid = Process::GetCurrentProcess();
		
		for (ProcessData& item : vec)
		{
			if (exepath == item.path && pid != item.id)
			{
#ifdef XG_LINUX
				ColorPrint(eYELLOW, "there is a running instance[%ld]\n", (long)(item.id));
#else
				MessageBoxW(NULL, L"当前应用已有一个正在运行的实例", L"正在运行", MB_OK | MB_SYSTEMMODAL | MB_ICONSTOP);
#endif
				ErrorExit(XG_OK);
			}
		}
	}
	else
	{
		ErrorExit(XG_SYSERR);
	}
}
bool Process::IsStartedByExplorer()
{
	static int flag = -1;

	if (flag >= 0) return flag > 0;

#ifdef XG_LINUX
	flag = 0;
#else
	string exepath;
	PROCESS_T handle = Process::GetParentProcess(Process::GetCurrentProcess());

	CHECK_FALSE_RETURN(handle > 0);
	CHECK_FALSE_RETURN(GetProcessExePath(handle, exepath));

	if (string::npos == stdx::tolower(exepath).find("explorer.exe"))
	{
		flag = 0;
	}
	else
	{
		flag = 1;
	}
#endif

	return flag > 0;
}
bool Process::SetCommonExitSignal()
{
#ifdef XG_LINUX
	signal(SIGINT, CommonExitFunc);
	signal(SIGHUP, CommonExitFunc);
	signal(SIGQUIT, CommonExitFunc);
	signal(SIGTERM, CommonExitFunc);
#endif

	return true;
}
void Process::CommonExitFunc(int signum)
{
#ifdef XG_LINUX
	if (signum == SIGHUP)
	{
		close(STDIN_FILENO);
		close(STDOUT_FILENO);
		close(STDERR_FILENO);
		signal(signum, SIG_IGN);

		return;
	}
#endif

	if (GetApplication()) GetApplication()->clean();

#ifdef XG_LINUX
	sync();
	sleep(1);
	_exit(signum);
#else
	TerminateProcess(::GetCurrentProcess(), signum);
#endif
}
bool Process::SetCurrentDirectory(const string& path)
{
#ifdef XG_LINUX
	return chdir(path.c_str()) >= 0;
#else
	string tmp = stdx::syscode(path);

	if (tmp.back() == ':') tmp.push_back('/');

	return SetCurrentDirectoryA(tmp.c_str()) ? true : false;
#endif
}
bool Process::SetEnv(const string& key, const string& val)
{
#ifdef XG_LINUX
	return setenv(key.c_str(), val.c_str(), 1) == 0;
#else
	return SetEnvironmentVariableA(key.c_str(), stdx::syscode(val).c_str()) ? true : false;
#endif
}
string Process::GetEnv(const string& key)
{
#ifdef XG_LINUX
	const char* env = getenv(key.c_str());

	if (env == NULL) return stdx::EmptyString();

	return env;
#else
	char env[1024] = {0};

	if (GetEnvironmentVariableA(key.c_str(), env, sizeof(env) - 1) > 0) return stdx::utfcode(env);

	return stdx::EmptyString();
#endif
}
bool Process::RegisterLibraryPath(const string& path)
{
#ifdef XG_LINUX
	string key = "LD_LIBRARY_PATH";
#else
	string key = "PATH";
#endif
	if (path.empty())
	{
		return RegisterVariable(key, path::parent(proc::exepath()));
	}
	else
	{
		return RegisterVariable(key, path);
	}
}
bool Process::RegisterExecutablePath(const string& path)
{
	if (path.empty())
	{
		return RegisterVariable("PATH", path::parent(proc::exepath()));
	}
	else
	{
		return RegisterVariable("PATH", path);
	}
}
bool Process::RegisterVariable(const string& key, const string& val)
{
#ifdef XG_LINUX
	string gap = ":";
	string str = val;
#else
	string gap = ";";
	string str = stdx::replace(val, "/", "\\");
#endif
	string tmp = stdx::trim(GetEnv(key), gap);

	if (tmp.empty())
	{
		tmp = str;
	}
	else if (tmp.find(str) == string::npos)
	{
		tmp += gap + str;
	}

	return SetEnv(key, tmp);
}
PROCESS_T Process::GetParentProcess(PROCESS_T handle)
{
	vector<ProcessData> vec;

	if (GetSystemProcessList(vec) <= 0) return XG_ERROR;

	for (auto& item : vec)
	{
		if (handle == item.id) return item.pid;
	}

	return XG_ERROR;
}
int Process::GetSystemProcessListByName(vector<ProcessData>& vec, const string& name)
{
	vector<ProcessData> tmp;

	if (GetSystemProcessList(tmp) < 0) return XG_ERROR;

	vec.clear();

	for (auto& item : tmp)
	{
		if (name == item.name) vec.push_back(item);
	}

	return vec.size();
}
int Process::GetSystemProcessListByExeName(vector<ProcessData>& vec, const string& path)
{
	vector<ProcessData> tmp;

	GetSystemProcessList(tmp);

	vec.clear();

	for (auto& item : tmp)
	{
		if (path == item.path) vec.push_back(item);
	}

	return vec.size();
}
bool Process::GetProcessExePath(string& path)
{
	path = stdx::GetProcessExePath();

	return path.length() > 0;
}
bool Process::SetDaemonCommand(const string& cmd)
{
	char* data;
	Sharemem shm;

	CHECK_FALSE_RETURN(data = (char*)shm.open(GetEnv(DAEMON_ENVNAME)));

	strncpy(data, cmd.c_str(), 1000);

	return true;
}

#ifdef XG_LINUX

#include <signal.h>

PROCESS_T Process::GetCurrentProcess()
{
	static PROCESS_T pid = (PROCESS_T)(getpid());

	return pid;
}
string Process::GetCurrentDirectory()
{
	char buffer[MAX_PATH];

	if (getcwd(buffer, sizeof(buffer)) == NULL) return stdx::EmptyString();

	return buffer;
}
bool Process::Wait(PROCESS_T handle)
{
	return waitpid(handle, NULL, 0) > 0;
}
bool Process::Kill(PROCESS_T handle, int flag)
{
	return kill(handle, flag > 0 ? flag : SIGKILL) == 0;
}
bool Process::InitDaemon()
{
	PROCESS_T handle = fork();

	if (handle == 0)
	{
		setsid();
	}
	else if (handle > 0)
	{
		ErrorExit(0);
	}

	return handle >= 0;
}
bool Process::GetProcessExePath(PROCESS_T handle, string& path)
{
	char buffer[MAX_PATH] = {0};

	sprintf(buffer, "/proc/%d/exe", (int)(handle));

	if (readlink(buffer, buffer, sizeof(buffer)) > 0)
	{
		size_t pos = (path = buffer).find(" (deleted)");

		if (pos != string::npos && pos + 10 == path.length()) path = path.substr(0, pos);
	}
	else
	{
		path.clear();
	}
	
	return path.length() > 0;
}
int Process::GetSystemProcessList(vector<ProcessData>& vec)
{
	FILE* fp;
	ProcessData data;
	vector<string> v;
	char buffer[MAX_PATH];

	if (stdx::GetFolderContent(v, "/proc", ePATH) < 0) return XG_ERROR;

	vec.clear();

	for (string& item : v)
	{
		data.id = (PROCESS_T)(atoi(item.c_str()));

		sprintf(buffer, "/proc/%s/status", item.c_str());

		fp = fopen(buffer, "r");

		if (fp == NULL) continue;

		int num = 0;

		while (fgets(buffer, sizeof(buffer), fp))
		{
			if (memcmp(buffer, "Name:", 5) == 0)
			{
				++num;

				data.name = stdx::trim(buffer + 5);
			}
			else if (memcmp(buffer, "PPid:", 5) == 0)
			{
				++num;

				data.pid = (PROCESS_T)(atoi(stdx::trim(buffer + 5).c_str()));
			}

			if (num >= 2) break;
		}

		fclose(fp);

		if (num < 2) continue;

		data.path = stdx::EmptyString();

		GetProcessExePath(data.id, data.path);

		vec.push_back(data);
	}

	return vec.size();
}

#else

#include <tlhelp32.h>

#ifdef _MSC_VER
#pragma comment(lib,"advapi32.lib")
#endif

HANDLE Process::GetProcessHandle(PROCESS_T id)
{
	return OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, id);
}
bool Process::EnableDebugPrivilege(HANDLE handle)
{
	HANDLE token = NULL;
	TOKEN_PRIVILEGES tkp;

	tkp.PrivilegeCount = 1;

	auto task = [&](){
		CHECK_FALSE_RETURN(OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, &token));
		CHECK_FALSE_RETURN(LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid));

		tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

		CHECK_FALSE_RETURN(AdjustTokenPrivileges(token, FALSE, &tkp, sizeof(tkp), NULL, NULL));

		return true;
	};

	bool res = task();

	if (HandleCanUse(token)) Close(token);

	return res;
}
PROCESS_T Process::GetCurrentProcess()
{
	static PROCESS_T pid = GetCurrentProcessId();

	return pid;
}
string Process::GetCurrentDirectory()
{
	char buffer[MAX_PATH];
	DWORD len = GetCurrentDirectoryA(sizeof(buffer), buffer);

	if (len == 0 || len >= sizeof(buffer)) return stdx::EmptyString();

	return stdx::replace(stdx::utfcode(buffer), "\\", "/");
}
bool Process::Wait(PROCESS_T handle)
{
	bool res = true;
	HANDLE tmp = GetProcessHandle(handle);

	CHECK_FALSE_RETURN(HandleCanUse(tmp));

	if (WaitForSingleObject(tmp, INFINITE) == 0xFFFFFFFF) res = false;

	CloseHandle(tmp);

	return res;
}
bool Process::Kill(PROCESS_T handle, int flag)
{
	bool res = true;
	HANDLE tmp = GetProcessHandle(handle);
	
	CHECK_FALSE_RETURN(HandleCanUse(tmp));

	res = (TerminateProcess(tmp, flag) == TRUE);

	CloseHandle(tmp);

	return res;
}
bool Process::InitDaemon()
{
	fclose(stdout);
	
	return true;
}
bool Process::GetProcessExePath(PROCESS_T handle, string& path)
{
	char buffer[MAX_PATH];
	DWORD dw = sizeof(buffer);
	HANDLE tmp = GetProcessHandle(handle);

	CHECK_FALSE_RETURN(HandleCanUse(tmp));

	if (QueryFullProcessPath)
	{
		dw = QueryFullProcessPath(tmp, 0, buffer, &dw);
	}
	else
	{
		dw = GetModuleFileNameExA(tmp, NULL, buffer, dw);
	}

	CloseHandle(tmp);

	CHECK_FALSE_RETURN(dw > 0);

	path = stdx::replace(stdx::utfcode(buffer), "\\", "/");

	return true;
}
int Process::GetSystemProcessList(vector<ProcessData>& vec)
{
	HANDLE handle;
	ProcessData data;
	PROCESSENTRY32W pe;
	char buffer[MAX_PATH];

	pe.dwSize = sizeof(pe);
	handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

	if (handle == INVALID_HANDLE_VALUE) return XG_ERROR;

	BOOL flag = Process32FirstW(handle, &pe);

	while (flag)
	{
		if (pe.szExeFile[0] == 0)
		{
			buffer[0] = 0;
		}
		else
		{
			if (WideCharToMultiByte(CP_ACP, 0, pe.szExeFile, -1, buffer, (int)(wcslen(pe.szExeFile) * 2), NULL, NULL) == 0)
			{
				CloseHandle(handle);

				return XG_ERROR;
			}
		}

		data.name = buffer;
		data.id = pe.th32ProcessID;
		data.path = stdx::EmptyString();

		GetProcessExePath(data.id, data.path);

		data.pid = pe.th32ParentProcessID;
		vec.push_back(data);

		flag = Process32Next(handle, &pe);
	}

	CloseHandle(handle);

	return vec.size();
}

#endif

bool Application::main()
{
	return true;
}
void Application::clean()
{
}
void Application::puts(const string& msg)
{
	::puts(stdx::syscode(msg).c_str());
}
void Application::printf(const char* fmt, ...)
{
	string str;
	va_list args;

	va_start(args, fmt);
	stdx::vformat(str, fmt, args);
	va_end(args);

	str = stdx::syscode(str);
	fwrite(str.c_str(), 1, str.length(), stdout);
}

DllFile::DllFile()
{
	handle = NULL;
	ctime = 0;
}
DllFile::~DllFile()
{
	close();
}
void DllFile::close()
{
	if (handle) DllFileClose(handle);

	handle = NULL;
	ctime = 0;
	path.clear();
}
bool DllFile::open(const string& path)
{
	close();

	CHECK_FALSE_RETURN(Process::SetEnv("{LAST_OPEN_DLLPATH}", path));

	if (handle = DllFileOpen(path.c_str()))
	{
		this->ctime = time(NULL);
		this->path = path;

		return true;
	}
	
	string msg = DllFile::GetErrorString();

	if (msg.empty())
	{
		LogTrace(eERR, "load dynamic library[%s] failed", path.c_str());
	}
	else
	{
		LogTrace(eERR, "load dynamic library[%s] failed[%s]", path.c_str(), msg.c_str());
	}
	
	return false;
}
void* DllFile::getAddress(const string& name) const
{
	void* ptr = DllFileGetAddress(handle, name.c_str());

	if (ptr) return ptr;

	string msg = DllFile::GetErrorString();

	if (msg.empty())
	{
		LogTrace(eERR, "read dynamic link[%s][%s] failed", path.c_str(), name.c_str());
	}
	else
	{
		LogTrace(eERR, "read dynamic link[%s][%s] failed[%s]", path.c_str(), name.c_str(), msg.c_str());
	}
	
	return ptr;
}

SpinMutex* DllFile::GetMutex()
{
	XG_DEFINE_GLOBAL_VARIABLE(SpinMutex)
}
string DllFile::GetLastPath()
{
	return Process::GetEnv("{LAST_OPEN_DLLPATH}");
}
string DllFile::GetErrorString()
{
#ifdef XG_LINUX
	const char* msg = dlerror();
		
	return msg ? msg : stdx::EmptyString();
#else
	return ::GetErrorString();
#endif
}
bool DllFile::Remove(sp<DllFile> dll)
{
	CHECK_FALSE_RETURN(dll);

	return Remove(dll->getPath());
}
bool DllFile::Remove(const string& path)
{
	static SpinMutex& mtx = *GetMutex();
	static ObjectMap& dllmap = *GetObjectMap();

	SpinLocker lk(mtx);
	auto it = dllmap.find(path);

	if (it == dllmap.end()) return false;

	dllmap.erase(it);

	return true;
}
DllFile::ObjectMap* DllFile::GetObjectMap()
{
	XG_DEFINE_GLOBAL_VARIABLE(ObjectMap)
}
sp<DllFile> DllFile::Get(const string& path, bool created, bool saved)
{
	sp<DllFile> dll;
	static SpinMutex& mtx = *GetMutex();
	static ObjectMap& dllmap = *GetObjectMap();

	SpinLocker lk(mtx);
	auto it = dllmap.find(path);
	
	if (it == dllmap.end())
	{
		if (created)
		{
			dll = newsp<DllFile>();

			if (dll->open(path))
			{
				if (saved) dllmap.insert(pair<string, sp<DllFile>>(path, dll));
			}
			else
			{
				dll = NULL;
			}
		}
	}
	else
	{
		dll = it->second;
	}

	return dll;
}

int LineCommand::init(const string& cmd, int* endpos)
{
	if (cmd.empty()) return 0;

	string msg;
	vector<string> vec;
	const char* str = cmd.c_str();
	const char* end = strchr(str, '\n');

	if (end)
	{
		if (end > str && *(end - 1) == '\r')
		{
			msg = string(str, end - 1);
		}
		else
		{
			msg = string(str, end);
		}
	}
	else
	{
		msg = str;
	}
	
	if (endpos) *endpos = msg.length();

	msg = stdx::replace(msg, "\t", " ");
	msg = stdx::replace(msg, "\r", " ");
	stdx::split(vec, msg, " ");

	for (const string& val : vec)
	{
		if (val.empty()) continue;

		if ((val[0] == '\'' && val.back() == '\'') || ((val[0] == '\"' && val.back() == '\"')))
		{
			vec.push_back(string(val.c_str() + 1, val.length() - 2));
		}
		else
		{
			vec.push_back(val);
		}	
	}

	return vec.size();
}

ContentNode::ContentNode()
{
	valspliter = ";";
	keyspliter = ": ";
	endspliter = "\r\n";
}
void ContentNode::clear()
{
	content.clear();
	vec.clear();
}
bool ContentNode::setValue(const string& key, const string& val, bool append)
{
	auto it = content.find(key);

	if (it == content.end())
	{
		vec.push_back(key);
		content[key] = val;
	}
	else if (append)
	{
		it->second += valspliter + val;
	}
	else
	{
		it->second = val;
	}

	return true;
}
string& ContentNode::operator [] (const string& key)
{
	if (content.find(key) == content.end()) vec.push_back(key);

	return content[key];
}
const string& ContentNode::getValue(const string& key) const
{
	const auto it = content.find(key);

	if (it == content.end()) return stdx::EmptyString();

	return it->second;
}
bool ContentNode::parse(const string& msg, bool inited)
{
	size_t pos = 0;
	vector<string> tmp;

	if (inited) clear();

	CHECK_FALSE_RETURN(stdx::split(tmp, msg, endspliter) > 0);

	for (const string& val : tmp)
	{
		if ((pos = val.find(keyspliter)) == string::npos) continue;

		CHECK_FALSE_RETURN(setValue(val.substr(0, pos), val.substr(pos + keyspliter.length()), true));
	}

	return vec.size() > 0;
}
string ContentNode::toString() const
{
	string str;

	if (vec.empty()) return str;

	for (const string& key : vec)
	{
		str += endspliter + key + keyspliter + content.find(key)->second;
	}

	return str.c_str() + endspliter.length();
}
bool ContentNode::setOrder(const string& key, int idx)
{
	if (idx < 0) idx += vec.size();

	CHECK_FALSE_RETURN (idx >= 0 && idx < vec.size());

	for (size_t i = 0; i < vec.size(); i++)
	{
		if (key == vec[i])
		{
			if (idx == i) return true;

			vec[i] = vec[idx];
			vec[idx] = key;

			return true;
		}
	}

	return false;
}
///////////////////////////////////////////////////////////////////
#endif